package com.xgk.boot.module.infra.framework.file.core.client.oss;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.aliyun.oss.model.ObjectMetadata;
import com.xgk.boot.module.infra.framework.file.core.client.AbstractFileClient;
import com.xgk.boot.module.infra.framework.file.core.client.s3.FilePresignedUrlRespDTO;
import lombok.extern.slf4j.Slf4j;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.PutObjectRequest;
import org.apache.commons.io.IOUtils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.time.Duration;
import java.util.Date;

/**
 * 实现 阿里云 -OSS、等云服务
 * @author xgk
 */
@Slf4j
public class OSSFileClient extends AbstractFileClient<OSSFileClientConfig> {

    private OSS client;

    public OSSFileClient(Long id, OSSFileClientConfig config) {
        super(id, config);
    }

    @Override
    protected void doInit() {
        // 补全 domain
        if (StrUtil.isEmpty(config.getDomain())) {
            config.setDomain(buildDomain());
        }
        // 初始化 S3 客户端
        client = new OSSClientBuilder().build(config.getEndpoint(), config.getAccessKey(), config.getAccessSecret());

    }

    @Override
    public String upload(byte[] content, String path, String type) {
        // 1. 将字节数组转换为输入流
        ByteArrayInputStream inputStream = new ByteArrayInputStream(content);
        // 2. 创建上传对象的元数据
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentLength(content.length);
        metadata.setContentType(type);
        // 构造 PutObjectRequest
        PutObjectRequest putRequest = new PutObjectRequest(
                config.getBucket(),
                path,
                inputStream,
                metadata
        );
        // 执行上传
        try {
            client.putObject(putRequest);
        } catch (Exception e) {
            log.error("[upload][path({}) 上传失败]", path, e);
            throw e;
        }
        // 拼接返回路径
        return config.getDomain() + "/" + path;
    }

    @Override
    public String upload(InputStream inputStream, String path, String contentType) {
        InputStream newInputStream = null;
        try {
            byte[] content = IOUtils.toByteArray(inputStream);
            long length = content.length;
            newInputStream = new ByteArrayInputStream(content);

            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentLength(length);
            metadata.setContentType(contentType);
            // 构造 PutObjectRequest
            PutObjectRequest putRequest = new PutObjectRequest(
                    config.getBucket(),
                    path,
                    newInputStream,
                    metadata
            );
            // 执行上传
            client.putObject(putRequest);
            return config.getDomain() + "/" + path;

        } catch (IOException e) {
            log.error("[upload][path({}) 上传失败]", path, e);
            throw new RuntimeException(e);
        } finally {
            // OSS SDK会负责关闭输入流，无需手动关闭
            if(inputStream !=null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                }
            }
            if(newInputStream!=null){
                try {
                    newInputStream.close();
                } catch (IOException e) {
                }
            }
        }
    }

    @Override
    public String upload(InputStream inputStream, String path, String contentType, Long size) {
        try {
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentLength(size);
            metadata.setContentType(contentType);
            // 构造 PutObjectRequest
            PutObjectRequest putRequest = new PutObjectRequest(
                    config.getBucket(),
                    path,
                    inputStream,
                    metadata
            );
            // 执行上传
            client.putObject(putRequest);
            return config.getDomain() + "/" + path;
        } catch (Exception e) {
            log.error("[upload][path({}) 上传失败]", path, e);
            throw e;
        } finally {
            if(inputStream !=null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    @Override
    public void delete(String path) {
        try {
            client.deleteObject(config.getBucket(), path);
        } catch (Exception e) {
            log.error("[delete][path({}) 删除失败]", path, e);
            throw e;
        }
    }

    @Override
    public byte[] getContent(String path) {
        try {
            OSSObject ossObject = client.getObject(config.getBucket(), path);
            return IoUtil.readBytes(ossObject.getObjectContent());
        } catch (Exception e) {
            log.error("[getContent][path({}) 获取内容失败]", path, e);
            throw e;
        }
    }

    @Override
    public FilePresignedUrlRespDTO getPresignedObjectUrl(String path) {
        Duration expiration = Duration.ofHours(24);
        return new FilePresignedUrlRespDTO(getPresignedUrl(path, expiration), config.getDomain() + "/" + path);
    }

    /**
     * 生成动态的预签名上传 URL
     *
     * @param path       相对路径
     * @param expiration 过期时间
     * @return 生成的上传 URL
     */
    private String getPresignedUrl(String path, Duration expiration) {
        try {
            Date expirationDate = new Date(System.currentTimeMillis() + expiration.toMillis());
            URL url = client.generatePresignedUrl(config.getBucket(), path, expirationDate);
            return url.toString();
        } catch (Exception e) {
            log.error("[getPresignedUrl][path({}) 生成预签名URL失败]", path, e);
            throw e;
        }
    }

    /**
     * 基于 bucket + endpoint 构建访问的 Domain 地址
     *
     * @return Domain 地址
     */
    private String buildDomain() {
        // 如果已经是 http 或者 https，则不进行拼接.主要适配 MinIO
        if (HttpUtil.isHttp(config.getEndpoint()) || HttpUtil.isHttps(config.getEndpoint())) {
            return StrUtil.format("{}/{}", config.getEndpoint(), config.getBucket());
        }
        return StrUtil.format("https://{}.{}", config.getBucket(), config.getEndpoint());
    }

}
